package sk.tuke.constraintbuilder.analyzer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;

import sk.tuke.constraintbuilder.model.Model;
import sk.tuke.constraintbuilder.model.Ruleset;
import sk.tuke.constraintbuilder.utils.MyJdtUtils;
import sk.tuke.constraintbuilder.utils.MyPreferences;

public class RulesetManager {
	
	private static RulesetManager instance;
	private ArrayList<RulesetRegisterItem> rulesetRegister = new ArrayList<RulesetRegisterItem>();
	private Map<IResource, HashSet<IResource>> subClasses = new HashMap<>();
	
	public static RulesetManager getInstance(){
		if(instance==null){
			instance = new RulesetManager();
			instance.loadRegisterFromFile();
		}
		return instance;
	}
	
	public void applyRulesetsOnProject(IJavaProject javaProject, Set<Ruleset> rulesets){
		for (Ruleset ruleset : rulesets) {
			applyRulesetOnProject(javaProject.getProject(), ruleset);
		}
	}

	public void applyRulesetOnProject(IProject project, Ruleset ruleset){
		if(JavaCore.create(project) instanceof IJavaProject){
			String projectName = project.getName();
			RulesetRegisterItem registerItem = null;
			for (RulesetRegisterItem item : rulesetRegister) {
				if(item.getProjectName().equals(projectName)){
					registerItem = item;
				}
			}
			if(registerItem==null){
				registerItem = new RulesetRegisterItem();
				registerItem.setProjectName(projectName);
				rulesetRegister.add(registerItem);
			}
			registerItem.getRulesets().add(ruleset.getName());
			registerItem.setSchema(createSchemaForProject(project));
			saveProjectListToFile();
			
			try {
				Analyzer analyzer = new Analyzer(new File(registerItem.getSchema()));
				for (IFile javaFile : MyJdtUtils.getJavaFilesFromProject(project)) {
					analyzer.examineResource(javaFile);
				}
			} catch (CoreException e) {
				System.out.println("Initial analyze failed! ");
				e.printStackTrace();
			}
		}
	}
	
	private String createSchemaForProject(IProject project){
		Set<Ruleset> rulesets = getRulesets(project.getName());
		if(rulesets!=null){
			File schemaFile = new SchemaGenerator().generateXmlSchema(rulesets,project.getName());
			return schemaFile.getAbsolutePath();
		}
		else{
			return null;
		}
	}
	
	public void removeRulesetFromProject(IProject project, Ruleset ruleset){
		for (RulesetRegisterItem item : rulesetRegister) {
			if(item.getProjectName().equals(project.getName())){
				item.getRulesets().remove(ruleset.getName());
				
				createSchemaForProject(project);
				
				try {
					Analyzer analyzer = new Analyzer(new File(item.getSchema()));
					for (IFile javaFile : MyJdtUtils.getJavaFilesFromProject(project)) {
						analyzer.examineResource(javaFile);
					}
				} catch (CoreException e) {
					System.out.println("Initial analyze failed! ");
					e.printStackTrace();
				}
				saveProjectListToFile();
			}
		}
		
	}
	
	private void saveProjectListToFile() {
		try {
			File file = new File(MyPreferences.REGISTER_FILE);
			FileOutputStream fos = new FileOutputStream(file);
			ObjectOutputStream output = new ObjectOutputStream(fos);
			output.writeObject((Serializable)rulesetRegister);
			output.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	
	private void loadRegisterFromFile() {
		try {
			File file = new File(MyPreferences.REGISTER_FILE);
			FileInputStream fis = new FileInputStream(file);
			ObjectInputStream input = new ObjectInputStream(fis);
			Object obj = input.readObject();
			if(obj instanceof ArrayList<?>){
				rulesetRegister = (ArrayList<RulesetRegisterItem>) obj;
			}
			input.close();
		} catch (ClassNotFoundException | IOException e) {
			//File not exists - we can ignore it, register will be empty
		}	
	}
	
	public Set<Ruleset> getRulesets(String projectName){
		RulesetRegisterItem registerItem = null;	
		for (RulesetRegisterItem item : rulesetRegister) {
			if(item.getProjectName().equals(projectName)){
				registerItem = item;
				Set<Ruleset> rulesets = new HashSet<>();
				for (String rulesetName : registerItem.getRulesets()) {
					Ruleset ruleset = Model.getModel().getRulesetByName(rulesetName);
					if(ruleset!=null){
						rulesets.add(ruleset);
					}
				}
				return rulesets;
			}
		}
		return null;
	}
	
	public File getSchema(String projectName){
		for (RulesetRegisterItem item : rulesetRegister) {
			if(item.getProjectName().equals(projectName)){
				File schema = new File(item.getSchema());
				if(schema.exists()){
					return schema;
				}
			}
		}
		return null;
	}
	
	public List<RulesetRegisterItem> getProjectList(){
		return rulesetRegister;
	}
	
	public void setSubClassOrImplementation(IResource subClass, IResource superClass){
		HashSet<IResource> list = subClasses.get(superClass);
		if(list==null){
			list = new HashSet<>();
			subClasses.put(superClass, list);
		}
		list.add(subClass);
	}
	
	public HashSet<IResource> getSubClassesForResource(IResource resource){
		return subClasses.get(resource);
	}
}


